#ifdef WIN32

inline void Assert(bool statement, const char *description)
{
    if(!statement)
    {
        MessageBox(NULL, description, "Fatal Problem Encountered", MB_OK);
#ifdef _DEBUG
        __asm int 3;
#endif
        ExitProcess(1);
    }
}

inline void Assert(bool statement, const string &description)
{
    if(!statement)
    {
        MessageBox(NULL, description.c_str(), "Fatal Problem Encountered", MB_OK);
#ifdef _DEBUG
        __asm int 3;
#endif
        ExitProcess(1);
    }
}

#else

inline void Assert(bool statement, const char *description)
{
    assert(statement);
}

inline void Assert(bool statement, const string &description)
{
    assert(statement);
}

#endif

namespace Utility
{
    template<class type>
    vector<type> ListToVector(const list<type> &l)
    {
        vector<type> result(l.begin(), l.end());
        return result;
    }

    //
    // This hash function taken from http://www.burtleburtle.net/bob/hash/doobs.html
    //

    inline void HashMix(UINT &a, UINT &b, UINT &c)
    {
        a -= b; a -= c; a ^= (c>>13);
        b -= c; b -= a; b ^= (a<<8);
        c -= a; c -= b; c ^= (b>>13);
        a -= b; a -= c; a ^= (c>>12);
        b -= c; b -= a; b ^= (a<<16);
        c -= a; c -= b; c ^= (b>>5);
        a -= b; a -= c; a ^= (c>>3);
        b -= c; b -= a; b ^= (a<<10);
        c -= a; c -= b; c ^= (b>>15);
    }

    inline UINT Hash32(const BYTE *k, UINT Length)
    {
        UINT a, b, c, LocalLength;

        /* Set up the internal state */
        LocalLength = Length;
        a = b = 0x9e3779b9;  /* the golden ratio; an arbitrary value */
        c = 0x9b9773e9;

        /*---------------------------------------- handle most of the key */
        while (LocalLength >= 12)
        {
            a += (k[0] + ((UINT)k[1]<<8) + ((UINT)k[2]<<16) + ((UINT)k[3]<<24));
            b += (k[4] + ((UINT)k[5]<<8) + ((UINT)k[6]<<16) + ((UINT)k[7]<<24));
            c += (k[8] + ((UINT)k[9]<<8) + ((UINT)k[10]<<16)+ ((UINT)k[11]<<24));
            HashMix(a, b, c);
            k += 12;
            LocalLength -= 12;
        }

        /*------------------------------------- handle the last 11 bytes */
        c += Length;
        switch(LocalLength)              /* all the case statements fall through */
        {
            case 11: c += ((UINT)k[10]<<24);
            case 10: c += ((UINT)k[9]<<16);
            case 9 : c += ((UINT)k[8]<<8);
        /* the first byte of c is reserved for the length */
            case 8 : b += ((UINT)k[7]<<24);
            case 7 : b += ((UINT)k[6]<<16);
            case 6 : b += ((UINT)k[5]<<8);
        case 5 : b += k[4];
            case 4 : a += ((UINT)k[3]<<24);
            case 3 : a += ((UINT)k[2]<<16);
            case 2 : a += ((UINT)k[1]<<8);
        case 1 : a += k[0];
        /* case 0: nothing left to add */
        }
        HashMix(a, b, c);
        /*-------------------------------------------- report the result */
        return c;
    }

    inline UINT64 Hash64(const BYTE *k, UINT Length)
    {
        UINT a, b, c, LocalLength;

        /* Set up the internal state */
        LocalLength = Length;
        a = b = 0x9e3779b9;  /* the golden ratio; an arbitrary value */
        c = 0x9b9773e9;

        /*---------------------------------------- handle most of the key */
        while (LocalLength >= 12)
        {
            a += (k[0] + ((UINT)k[1]<<8) + ((UINT)k[2]<<16) + ((UINT)k[3]<<24));
            b += (k[4] + ((UINT)k[5]<<8) + ((UINT)k[6]<<16) + ((UINT)k[7]<<24));
            c += (k[8] + ((UINT)k[9]<<8) + ((UINT)k[10]<<16)+ ((UINT)k[11]<<24));
            HashMix(a, b, c);
            k += 12;
            LocalLength -= 12;
        }

        /*------------------------------------- handle the last 11 bytes */
        c += Length;
        switch(LocalLength)              /* all the case statements fall through */
        {
            case 11: c += ((UINT)k[10]<<24);
            case 10: c += ((UINT)k[9]<<16);
            case 9 : c += ((UINT)k[8]<<8);
        /* the first byte of c is reserved for the length */
            case 8 : b += ((UINT)k[7]<<24);
            case 7 : b += ((UINT)k[6]<<16);
            case 6 : b += ((UINT)k[5]<<8);
        case 5 : b += k[4];
            case 4 : a += ((UINT)k[3]<<24);
            case 3 : a += ((UINT)k[2]<<16);
            case 2 : a += ((UINT)k[1]<<8);
        case 1 : a += k[0];
        /* case 0: nothing left to add */
        }
        HashMix(a, b, c);
        /*-------------------------------------------- report the result */
        return UINT64(c) + UINT64(UINT64(a) << 32);
    }

    template <class type> inline UINT32 Hash32(const type &Obj)
    {
        return Hash32((const BYTE *)&Obj, sizeof(type));
    }
    template <class type> inline UINT64 Hash64(const type &Obj)
    {
        return Hash64((const BYTE *)&Obj, sizeof(type));
    }

    bool FileExists(const string &filename);

    inline string IntToString(int i)
    {
        ostringstream S(ostringstream::out);
        S << i;
        return S.str();
    }

    inline string ZeroPadInteger(int i, int padding)
    {
        string s = IntToString(i);
        while(int(s.size()) < padding)
        {
            s = "0" + s;
        }
        return s;
    }

    inline FILE* CheckedFOpen(const char *filename, const char *mode)
    {
        FILE *file = fopen(filename, mode);
        Assert(file != NULL && !ferror(file), string("Failed to open file: ") + filename);
        return file;
    }

    inline void CheckedFRead(void *dest, UINT elementSize, UINT elementCount, FILE *file)
    {
        size_t elementsRead = fread(dest, elementSize, elementCount, file);
        Assert(!ferror(file) && elementsRead == elementCount, "fread failed");
    }

    inline void CheckedFWrite(const void *src, UINT elementSize, UINT elementCount, FILE *file)
    {
        size_t elementsWritten = fwrite(src, elementSize, elementCount, file);
        Assert(!ferror(file) && elementsWritten == elementCount, "fwrite failed");
    }

#ifdef WIN32
    inline UINT GetFileSize(const string &filename)
    {
        BOOL success;
        WIN32_FILE_ATTRIBUTE_DATA info;
        success = GetFileAttributesEx(filename.c_str(), GetFileExInfoStandard, (void*)&info);
        Assert(success && info.nFileSizeHigh == 0, string("GetFileAttributesEx failed on ") + filename);
        return info.nFileSizeLow;
    }
#else
    inline UINT GetFileSize(const string &filename)
    {
        Assert(false, "Someone will have to find out how to get the size of a file from unistd, I'm sure it's fairly trivial.");
    }
#endif

    inline vector<BYTE> GetFileData(const string &filename)
    {
        FILE *inputFile = Utility::CheckedFOpen(filename.c_str(), "rb");
        UINT fileSize = Utility::GetFileSize(filename);
        vector<BYTE> result(fileSize);
        Utility::CheckedFRead(&(result[0]), sizeof(BYTE), fileSize, inputFile);
        fclose(inputFile);
        return result;
    }

    inline int BoolToInt(bool b)
    {
        if(b) return 1;
        return 0;
    }

    inline UINT64 MakeInt64(int a, int b)
    {
        UINT64 result = 0;
        ((int *)&result)[0] = a;
        ((int *)&result)[1] = b;
        return result;
    }

    inline vector<string> GetFileLines(ifstream &file, UINT minLineLength)
    {
        vector<string> result;
        string curLine;
        while(!file.fail())
        {
            getline(file, curLine);
            if(!file.fail() && curLine.length() >= minLineLength)
            {
                result.push_back(curLine);
            }
        }
        return result;
    }

    inline vector<string> GetFileLines(const string &filename, UINT minLineLength = 0)
    {
        ifstream file(filename.c_str());
        Assert(!file.fail(), string("Failed to open ") + filename);
        return GetFileLines(file, minLineLength);
    }
}
